home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / boot / netBoot.OpenProm / tftp.c < prev   
Encoding:
C/C++ Source or Header  |  1991-07-30  |  9.5 KB  |  375 lines

  1. /*
  2.  * tftp.c
  3.  *
  4.  * @(#)tftp.c 1.9 88/02/08 Copyr 1986 Sun Micro
  5.  * Copyright (c) 1986 by Sun Microsystems, Inc.
  6.  *
  7.  * Standalone network boot via TFTP
  8.  */
  9. #include "boot.h"
  10. #include "sainet.h"
  11. #include <arpa/tftp.h>
  12. #include <sys/exec.h>
  13.  
  14. #ifndef    IPPORT_TFTP
  15. #define IPPORT_TFTP    69
  16. #endif
  17.  
  18. char    *tftp_errs[] = {
  19.     "not defined",
  20.     "file not found",
  21.     "access violation",
  22.     "disk full or allocation exceeded",
  23.     "illegal TFTP operation",
  24.     "unknown transfer ID",
  25.     "file already exists",
  26.     "no such user"
  27. };
  28.  
  29. struct tftp_pack {
  30.     Net_IPHeader tf_ip;        /* IP header */
  31.     Net_UDPHeader tf_udp;        /* UDP header */
  32.     struct tftphdr tf_tftp;        /* TFTP header */
  33.     char tftp_data[SEGSIZE];    /* TFTP data beyond header */
  34. };
  35.  
  36. typedef union {
  37.     char buf[NET_ETHER_MAX_BYTES + 4];
  38.     struct {
  39.     Net_EtherHdr header;
  40.     Net_IPHeader ip;        /* IP header */
  41.     } aligned;
  42. } PacketBuffer;
  43.                     /* TFTP packet */
  44. /*
  45.  * Size of Headers in TFTP DATA packet
  46.  */
  47. #define TFTPHDRLEN    (sizeof (Net_EtherHdr) + sizeof (Net_IPHeader) + \
  48.              sizeof (Net_UDPHeader) + 4)
  49.  
  50. struct tftpglob {
  51.     PacketBuffer    tf_out;        /* outgoing TFTP packet */
  52.     PacketBuffer    tf_tmpbuf;    /* tmp for incoming packets */
  53.     struct sainet tf_inet;        /* Internet state */
  54.     int    tf_block;        /* current block number */
  55.     char    *tf_data;        /* current load pointer */
  56. };
  57.  
  58. static struct tftpglob tfbuf;
  59.  
  60. #define    REXMIT_MSEC    4000        /* 4 seconds between retransmits */
  61.  
  62. /*
  63.  * Description: Entry point for initializing the ethernet during boot.
  64.  *
  65.  * Synopsis:    status = etheropen(fileId)
  66.  *        status    :(int) 0 command complete
  67.  *        fileId    :(void *) opaque pointer to PROM device
  68.  *
  69.  * Routines:    bzero, inet_init
  70.  */
  71. etheropen(fileId)
  72.     register void *fileId;
  73. {
  74.     register struct tftpglob *tf = &tfbuf;
  75.  
  76.     bzero((caddr_t)tf, sizeof(*tf));    /* clear tftp work space */
  77.  
  78.     /* get internet address */
  79.     inet_init(fileId, &tf->tf_inet, &tf->tf_tmpbuf);
  80.     return (0);
  81. }
  82.  
  83.  
  84. /*
  85.  * Description: Loads the boot routine across the ethernet
  86.  *
  87.  * Synopsis:    status = tftpload(fileId, fileName, unitNum)
  88.  *        status    :(int)    load address
  89.  *                  -1 error
  90.  *        fileId    :(void *) opaque pointer to PROM device (sun4c)
  91.  *                  or pointer to saioreq structure (sun4)
  92.  *        fileName:(char *) remote file to transfer
  93.  *        unitNum    :(int)    autoboot unit number
  94.  *
  95.  * Routines:    bzero
  96.  *
  97.  * Variables:    locked    :(int) lock in host and server
  98.  *        firsttry:(int) first try flag for autoboot
  99.  */
  100. tftpload(fileId, fileName, unitNum)
  101.     register void *fileId;
  102.     char *fileName;
  103.     int unitNum;
  104. {
  105.     register struct tftpglob    *tf = &tfbuf;
  106.     register struct tftp_pack   *in =
  107.         (struct tftp_pack *)((caddr_t)&tf->tf_tmpbuf.aligned.ip);
  108.     register struct tftp_pack   *out =
  109.         (struct tftp_pack *)((caddr_t)&tf->tf_out.aligned.ip);
  110.     register char            *p, *q, *x;
  111.     register short            i, len;
  112.     int                autoboot = 0;
  113.     int                firsttry = 0;
  114.     int                feedback = 0;
  115.     int finished = 0;
  116.     int                delay = REXMIT_MSEC;
  117.     int                time, xcount, locked, retry;
  118.     char                *ind = ".oOo";
  119.     int                feedbackCount = 0;
  120.     struct exec *header;
  121.                         /* if unit # is 0, this is
  122.                          * an autoboot */
  123.     if (unitNum == 0)
  124.                 autoboot = 1;
  125. top:
  126.                         /* Initialize IP header */
  127.     out->tf_ip.version = NET_IP_VERSION;    /* IP version number */
  128.     out->tf_ip.headerLen = sizeof(Net_IPHeader) / 4;/* header length */
  129.     out->tf_ip.timeToLive = NET_IP_MAX_TTL;        /* time to live */
  130.     out->tf_ip.protocol = NET_IP_PROTOCOL_UDP;    /* type of protocol */
  131.  
  132.                         /* set source address */
  133.  
  134.     bcopy(&tf->tf_inet.myAddr, &out->tf_ip.source, sizeof(out->tf_ip.source));
  135.      
  136.                         /* set destination address,
  137.                          * Dst host is argument with
  138.                          * our net number plugged in */
  139.     if (autoboot && firsttry == 0) {
  140.                         /* send to host from revarp */
  141.  
  142.         inet_copy(&out->tf_ip.dest, &tf->tf_inet.hisAddr);
  143.         firsttry = 1;
  144.     } else if (autoboot && firsttry > 0) {
  145.         Net_InetAddress tmpBroadcast = NET_INET_BROADCAST_ADDR;
  146.                         /* broadcast? */
  147.         inet_copy(&out->tf_ip.dest, &tmpBroadcast);
  148.     } else {
  149.                         /* unit specified */
  150.  
  151.         out->tf_ip.dest = out->tf_ip.source +
  152.                 unitNum  - in_lnaof(out->tf_ip.source);
  153.     }
  154.  
  155.                         /* initialize UDP header */
  156.  
  157.     out->tf_udp.srcPort =  (millitime() & 1023) + 1024;/* source post */
  158.     out->tf_udp.destPort =  IPPORT_TFTP;    /* destination port */
  159.     out->tf_udp.checksum =  0;        /* no checksum */
  160.  
  161.                         /* set tftpglob structure */
  162.     tf->tf_block = 1;
  163.     tf->tf_data = (char *)KERNEL_START;
  164.                         /* Create the TFTP Read Request
  165.                          * packet */
  166.     out->tf_tftp.th_opcode = RRQ;
  167.                         /* load internet address */
  168.     q = fileName;
  169.     p = out->tf_tftp.th_stuff;
  170.     while (*q && *q != ' ') {
  171.         *(p++) = *(q++);
  172.     }
  173.     *p++ = 0;
  174.         q = "octet";
  175.         while (*p++ = *q++)
  176.                 ;
  177.                                                 /* fill UDP packet */
  178.     out->tf_udp.len = sizeof (Net_UDPHeader) + 2 + 
  179.             (p - out->tf_tftp.th_stuff);
  180.                         /* fill ip packet */
  181.  
  182.     out->tf_ip.totalLen = sizeof (Net_IPHeader) + out->tf_udp.len;
  183.  
  184.                         /* init. transmit status */
  185.     locked = 0;
  186.     retry = 0;
  187.     time = millitime();
  188.                         /* transmit loop */
  189.     for (xcount = 0; xcount < 5;) {        /* try to xmit 5 times */
  190.         if (millitime() >= time) {
  191.             time = millitime() + delay;
  192.  
  193.                         /* limit delay to 64 sec */
  194.             delay = delay < 64000 ? delay * 2 : 64000;
  195.  
  196.                         /* show activity */
  197.             if ((feedbackCount++ % 8) == 0) 
  198.                 printf("%c\b", ind[feedback++ % 4]);
  199.  
  200.                         /* transmit */
  201.  
  202.             if (ip_output(fileId, 
  203.                 (caddr_t)out - sizeof(Net_EtherHdr),
  204.                 out->tf_ip.totalLen + sizeof (Net_EtherHdr),
  205.                 &tf->tf_inet, (caddr_t)&tf->tf_tmpbuf))
  206.                 printf("X\b");
  207.                         /* 5 times if not locked */
  208.             if (locked == 0 || retry > 15)
  209.                 xcount++;
  210.             else 
  211.                 retry++;
  212.         }
  213.                         /* get input IP packet */
  214.  
  215.         len = ip_input(fileId, (caddr_t)in - sizeof(Net_EtherHdr),
  216.             &tf->tf_inet);
  217.  
  218.                         /* check length of packet */
  219.         if (len < TFTPHDRLEN) {
  220. #ifdef debugjl
  221. /*printf(" not tftp packet\n");*/
  222. #endif debugjl
  223.             continue;
  224.         }
  225.                         /* check packet type */
  226.  
  227.         if (in->tf_ip.protocol != NET_IP_PROTOCOL_UDP ||
  228.             in->tf_udp.destPort != out->tf_udp.srcPort)  {
  229. #ifdef debugjl
  230. printf(" wrong packet type = %x\n",in->tf_ip.protocol);
  231. #endif debugjl
  232.             continue;
  233.             }
  234.                         /* dst has been locked in */
  235.         if (locked &&
  236.                     out->tf_ip.dest != in->tf_ip.source)
  237. #ifdef debugjl
  238.             { 
  239.             printf(" not locked address = %x%x%x%x\n",
  240.             out->tf_ip.dest.S_un.S_un_b.s_b1,
  241.             out->tf_ip.dest.S_un.S_un_b.s_b2,
  242.             out->tf_ip.dest.S_un.S_un_b.s_b3,
  243.             out->tf_ip.dest.S_un.S_un_b.s_b4);
  244.             continue;
  245.             }
  246. #else
  247.             continue;
  248. #endif debugjl
  249.                         /* error */
  250.  
  251.         if (in->tf_tftp.th_opcode == ERROR) {
  252.             if (autoboot && tf->tf_block == 1)
  253.                 continue;
  254.             if (in->tf_tftp.th_code < 0 ||
  255.                 in->tf_tftp.th_code > sizeof(tftp_errs)/sizeof(char *)){
  256.                 printf("tftp: Unknown error 0x%x\n",
  257.                     in->tf_tftp.th_code);
  258.             } else {
  259.                 printf("tftp: %s @ block %d\n",
  260.                     tftp_errs[in->tf_tftp.th_code], tf->tf_block);
  261.             }
  262.                         /* for autoboot, keep looping */
  263.             if (autoboot)
  264.                 goto top;
  265.             return (-1);
  266.         }
  267.                         /* we are looking for data */
  268.  
  269.         if (in->tf_tftp.th_opcode != DATA ||
  270.             in->tf_tftp.th_block  != tf->tf_block) 
  271. #ifdef debugjl
  272.             { 
  273.             printf(" not data packet");
  274.             continue;
  275.             }
  276. #else
  277.             continue;
  278. #endif debugjl
  279.                         /* in sequence DATA packet */
  280.         if (tf->tf_block == 1) {    
  281.                         /* lock onto server port */
  282.  
  283.             out->tf_udp.destPort = in->tf_udp.srcPort;
  284.  
  285.                         /* for autoboot, get address */
  286.             if (autoboot) 
  287.                 bcopy(&(in->tf_ip.source),&(out->tf_ip.dest),
  288.                     sizeof(in->tf_ip.source));
  289.  
  290.                         /* print server found */
  291.  
  292.             printf("Booting from tftp server at "); 
  293.                         inet_print(&out->tf_ip.dest);
  294.             locked = 1;
  295.         }
  296.                         /* calc. data length */
  297.  
  298.         len = in->tf_udp.len - (sizeof(Net_UDPHeader) + 4);
  299.  
  300.                         /* copy data to load point */
  301.         if (len) {
  302.             bcopy(in->tf_tftp.th_data, tf->tf_data, len);
  303.             if (tf->tf_block == 1) {
  304.                 header = (struct exec *)tf->tf_data;
  305.                 printf("Size: %d", header->a_text);
  306.             }
  307.             tf->tf_data += len;
  308.             if (header->a_text) {
  309.                 header->a_text -= len;
  310.                 if (tf->tf_block == 1) {
  311.                 /*
  312.                  * If on first block, don't count header size against the
  313.                  * text size.
  314.                  */
  315.                 header->a_text += sizeof(struct exec);
  316.                 }
  317.                 if ((int)header->a_text <= 0) {
  318.                 printf("+%d", header->a_data);
  319.                 header->a_data += header->a_text;
  320.                 header->a_text = 0;
  321.                 }
  322.             } else {
  323.                 header->a_data -= len;
  324.                 if ((int)header->a_data <= 0) {
  325.                 printf("+%d\n", header->a_bss);
  326.                 finished = 1;
  327.                 }
  328.             }
  329.         }
  330.                         /* send ACK (acknowledge) */
  331.         out->tf_tftp.th_opcode = ACK;
  332.         out->tf_tftp.th_block = tf->tf_block++;
  333.         out->tf_udp.len = sizeof (Net_UDPHeader) + 4;
  334.         out->tf_ip.totalLen =
  335.             sizeof (Net_IPHeader) + out->tf_udp.len;
  336.  
  337.                             /* transmit */
  338.  
  339.         if (ip_output(fileId, (caddr_t)out - sizeof(Net_EtherHdr),
  340.             out->tf_ip.totalLen + sizeof (Net_EtherHdr), &tf->tf_inet,
  341.             (caddr_t)&tf->tf_tmpbuf))
  342.             printf("X\b");
  343.                         /* reset count and retry */
  344.         xcount = 0;
  345.         retry = 0;
  346.         if ((feedbackCount++ % 8) == 0) 
  347.             printf("%c\b", ind[feedback++ % 4]); /* Show activity */
  348.  
  349.                         /* reset delay */
  350.         delay = REXMIT_MSEC;
  351.         time = millitime() + delay;
  352.                         /* check if end of file */
  353.         if ((len < SEGSIZE)  || finished) {
  354. #ifndef sun4
  355.             /*
  356.              * Zero out the uninitialized data, since Sprite
  357.              * doesn't do it for
  358.              * itself...
  359.              */
  360.             tf->tf_data += (int)header->a_data;
  361.             bzero(tf->tf_data, header->a_bss);
  362. #endif        
  363.             printf("Downloaded %d bytes from tftp server.\n\n",
  364.                 tf->tf_data - KERNEL_START);
  365.             return (KERNEL_START + sizeof(struct exec));
  366.         }
  367.     }
  368.     printf("tftp: time-out.\n");
  369.                         /* for autoboot, loop forever */
  370.     if (autoboot)
  371.         goto top;
  372.                         /* error return */
  373.     return (-1);
  374. }
  375.